The focus was on static ports with URIs set immediately after
the code was deployed. However, there exist a number of legitimate
cases where BizTalk does not know where to distribute a message until
additional runtime-only context is provided. For example, when you
configure a send port with an SMTP adapter in BizTalk Server, you are
required to explicitly provide the recipient's email address. Any time
this port is invoked, that particular email address is applied. But what
if the corresponding message
could be emailed to any of a
number of addresses? You could choose to set up a series of static send
ports and summon each one individually based on decision logic from the
orchestration. However, this is not a particularly flexible mechanism as
it requires changes to the orchestration whenever an email target is
added or removed. A better strategy is to apply dynamic ports and
perform a runtime query of the endpoint address. We could look up the
email recipient (via Business Rules, custom component, message value)
and set that value in a single spot within the orchestration. When
changes to the recipient list are necessary, the only thing that must
undergo a change is the user lookup mechanism and not the orchestration
itself.
Defining the service
Let's demonstrate how this
would work in a situation with services. A service client may invoke a
service and expect a response well after the initial connection has been
closed. To truly be loosely coupled and support multiple callers, our
solution should not hard-code the return address of the service inside
the invoked orchestration. Instead, to encourage reusability, this
orchestration should extract a reply to value from the message itself and dynamically set the return destination that it will use.
For this scenario, BizTalk
will accept data in, process it, and send a notification to an awaiting
service when processing is complete. Our first step is to define these
services that are anticipating a message from BizTalk. There is a simple
contract which expects a status update to be sent to the service endpoint.
[ServiceContract (Namespace="http://Seroter.BizTalkSOA.Chapter7")]
public interface IAdverseEvent
{
[OperationContract]
void UpdateAEStatus(AdverseEventStatus status);
}
[DataContract]
public class AdverseEventStatus
{
[DataMember]
public string AE_ID { get; set; }
[DataMember]
public AEStatusCode StatusCode { get; set; }
[DataMember]
public string Comments { get; set; }
}
public enum AEStatusCode
{
Received,
Pending,
DataError,
InReview,
Resolved
}
I've gone ahead and
created a pair of service classes which implement this interface and
write differing messages to the machine's Application Event Log.
public class AdverseEventService : IAdverseEvent
{
public void UpdateAEStatus(AdverseEventStatus status)
{
EventLog.WriteEntry(
"AE Client Application #1 (HTTP)",
"Status for AE " + status.AE_ID + " is " +
status.StatusCode.ToString());
}
}
Next we need to create a WCF Service Website project in Visual Studio.NET and define a pair of .svc
files whose Service directive points to the service class(es) we just
created. Our new service requires a valid application configuration, so I
created service definitions that utilized an HTTP endpoint for one
service and a NetTcpBinding
endpoint for the other. We
do not need to specify an address (or base address) for our services
when they are hosted by IIS 7.0. I also added a metadata behavior to
both so that BizTalk can interrogate the service for its contract.